<?php
/**
 * Created by PhpStorm
 * User: shen.zk
 * Date: 2023/9/4
 * Time: 17:58
 * Comment: ''
 */

namespace App\Service\Common;

use App\Common\ResponseCode;
use App\Exceptions\BusinessException;
use App\Service\BaseService;
use Illuminate\Support\Facades\Log;

class CheckSignService extends BaseService
{

    /**
     * 公钥验签
     * @param array $plainText
     * @param string $sign
     * @param string $publicKey
     * @return false|int
     * @author shen.zk
     * @date 2023-09-06 14:50
     */
    public function checkRequestSign(array $plainText ,string $sign,string $publicKey)
    {
        // 请求明文
        Log::info('[中间件]请求明文'.json_encode($plainText));
        // 公钥验签
        try {
            $resource = openssl_pkey_get_public($publicKey);
            $result = openssl_verify(json_encode($plainText), base64_decode($sign), $resource);
            openssl_free_key($resource);

            if (!$result)
                throw new BusinessException(ResponseCode::CHECKSIGNERROR);

            return $result;
        } catch (\Exception $e) {
            Log::info('[中间件]验证签名' . $e->getMessage());
            return false;
        }

    }

    /**
     * 私钥生成签名
     * @param array $params
     * @param string $privateKey
     * @return string
     * @throws \Exception
     * @author shen.zk
     * @date 2023-09-06 14:51
     */
    public function creteSign(array $params,string $privateKey)
    {
        $plainText = json_encode($params);
        Log::info("[加密数据]".json_encode($plainText));

        try {
            $resource = openssl_pkey_get_private($privateKey);
            $result   = openssl_sign($plainText, $sign, $resource);
            openssl_free_key($resource);
            if (!$result) throw new \Exception('sign error');
            return base64_encode($sign);
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * 使用自身平台私钥、服务方公钥创建签名
     * @param array $requestData
     * @param string $pubKey
     * @param string $priKey
     * @throws BusinessException
     * @author shen.zk
     * @date 2023-09-06 16:57
     */
    public function selfRequestCreateSign(array $requestData,string $pubKey,string $priKey ): array
    {
        $data = [];

        // 获取16位随机AES-KEY
        $key = $this->createAesGenerate(16);
        //Log::info("[加密]获取16位随机KEY:".$key);
        // 报文加密
        $data['data'] = $this->selfaesEncrypt($requestData,$key);
        //Log::info("[加密]报文加密".$data['data']);
        // 公钥加密随机16位AES-KEY
        $data['encryptKey'] = $this->selfrsaEncryptByPub($key, $pubKey);
        //Log::info("[加密]公钥加密随机16位AES-KEY".$data['encryptKey']);
        // 生成sign签名
        $data['sign'] = $this->selfPriCreateSign($data['data'], $priKey);
        //Log::info("[加密生成sign签名".$data['sign']);
        return $data;
    }

    /**
     * 随机生成AES-KEY
     * @param int $length
     * @return string
     * @author shen.zk
     * @date 2023-09-06 15:07
     */
    protected function createAesGenerate(int $length = 16)
    {
        $str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
        $arr = array();
        for ($i = 0; $i < $length; $i++) {
            $arr[] = $str[mt_rand(0, 61)];
        }

        return implode('', $arr);
    }

    /**
     * 报文使用随机key加密
     * @param array $plainText
     * @param string $key
     * @return string
     * @throws BusinessException
     * @author shen.zk
     * @date 2023-09-06 17:04
     */
    protected function selfaesEncrypt(array $plainText,string $key)
    {
        // ASCII排序
        ksort($plainText);

        // 转换JSON格式
        $plainText = json_encode($plainText,JSON_UNESCAPED_UNICODE);
        $ivlen = openssl_cipher_iv_length($cipher="AES-128-ECB");
        $iv = !$ivlen?"": openssl_random_pseudo_bytes($ivlen);

        // 报文加密
        $result = openssl_encrypt($plainText, 'AES-128-ECB', $key,OPENSSL_RAW_DATA,$iv);

        if (!$result) {
            Log::info('[加密]报文加密错误');
            throw new BusinessException(ResponseCode::CREATESIGNERROR);
        }

        // 返回BASE64编码
        return base64_encode($result);
    }

    /**
     * 公钥加密16位AES-KEY
     * @param string $aes_key
     * @param string $pub_key
     * @return string
     * @throws BusinessException
     * @author shen.zk
     * @date 2023-09-06 17:10
     */
    protected function selfrsaEncryptByPub(string $aesKey,string $pubKey)
    {
        if (!openssl_public_encrypt($aesKey, $cipherText, $pubKey, OPENSSL_PKCS1_PADDING)) {
            Log::info('[加密]AESKey 加密错误');
            throw new BusinessException(ResponseCode::CREATESIGNERROR);
        }

        return base64_encode($cipherText);
    }

    /**
     * 使用私钥对自身请求加密
     * @param $plainText
     * @param $path
     * @return string
     * @throws BusinessException
     * @author shen.zk
     * @date 2023-09-06 17:21
     */
    protected function selfPriCreateSign($plainText, $path)
    {
        try {
            $resource = openssl_pkey_get_private($path);
            $result = openssl_sign($plainText, $sign, $resource);
            openssl_free_key($resource);

            if (!$result) {
                Log::info('[加密]签名出错');
                throw new BusinessException(ResponseCode::CREATESIGNERROR);
            }

            return base64_encode($sign);
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * 公钥校验签名
     * @param string $responseParams
     * @param string $responseSign
     * @param string $pubKey
     * @return int
     * @throws BusinessException
     * @author shen.zk
     * @date 2023-09-06 17:32
     */
    public function verifySign(string $responseParams,string $responseSign,string $pubKey)
    {
        try {
            $resource = openssl_pkey_get_public($pubKey);
            $result = openssl_verify($responseParams, base64_decode($responseSign), $resource);
            openssl_free_key($resource);

            if (!$result) {
                Log::info('[解密]签名验证未通过');
                throw new BusinessException(ResponseCode::CHECKSIGNERROR);
            }

            return $result;
        } catch (\Exception $e) {
            Log::info('[解密]签名验证未通过' . $e->getMessage());
            throw new BusinessException(ResponseCode::CHECKSIGNERROR);
        }
    }

    /**
     * 私钥解密AESKey
     * @param string $encryptKey
     * @param string $priKey
     * @return string
     * @throws BusinessException
     * @author shen.zk
     * @date 2023-09-06 17:35
     */
    public function decryptByPri(string $encryptKey,string $priKey)
    {
        if (!openssl_private_decrypt(base64_decode($encryptKey), $plainText, $priKey, OPENSSL_PKCS1_PADDING)) {
            Log::info('[解密]AESKey 解密错误');
            throw new BusinessException(ResponseCode::CHECKSIGNERROR);
        }

        return (string)$plainText;
    }

    /**
     * AES解密返回内容
     * @param string $responseParams
     * @param string $decryptAESKey
     * @return string
     * @throws BusinessException
     * @author shen.zk
     * @date 2023-09-06 17:37
     */
    public function aesDecrypt(string $responseParams,string $decryptAESKey)
    {
        $result = openssl_decrypt(base64_decode($responseParams), 'AES-128-ECB', $decryptAESKey, 1);

        if (!$result) {
            Log::info('[解密]报文解密错误');
            throw new BusinessException(ResponseCode::CHECKSIGNERROR);
        }

        return $result;
    }
}
